終於要來講解相依性注入(dependency injection),一起來看看如何在 Blazor WebAssembly 中使用相依性注入吧!
相依性注入技術,可以說是當今 .NET Core 開發人員的必備技能,網路上也充斥著大量技術文章,避免有人還沒上車,就小小的聊一下,如果已經了解 DI 的人可以下滑至下個段落,或者可以看一下我理解對不對。
相依性注入簡單來說就是將物件的生命週期集中管理,這使得本來相依的兩個物件被迫分開,因為相依物件的生命週期從物件本身管理被改成由 DI Container 管理。當物件需要相依物件時,也需要從 DI Container 去要,而將相依物件放進 DI Container 與物件從 DI Container 取得相依物件,這整件事情,我們稱作相依性注入。
那麼為什麼會有相依性注入?我個人認為,是因為現在程式碼撰寫風格的變化,以前可能電腦沒那麼先進,所以對於系統的負擔錙銖必較,但現在除非是特定產業,不然細微的效能差異並不會影響使用者體驗,也造就了可讀性與可維護性較高的程式碼成為了現在的主流,當然也包括可測試性的程式碼,而這些程式碼都有個共通點,那就是符合 SOLID (物件導向設計) 的五大原則,而其中 D(依賴反轉原則) 的實現就是相依性注入。
因為我本人寫程式的資歷並不多,對於發展史也是東拼西湊,如果有誤導大眾瞎說了一堆還請提出,感謝。
相依性注入可以:
使多個元件間共用服務類別的單一實例(稱為單一服務)。
將元件與具體的服務類別間的參考變成抽象,也就是透過介面從 DI Container 中取得該介面的實作,使得我們可以交換實作來撰寫單元測試,也就是 Mock 的技巧。
在 Blazor WebAssembly 中的服務生命週期有:
存留期 | 描述 |
---|---|
Scoped | Blazor WebAssembly 應用程式目前不具有 DI 範圍的概念,註冊為 Scoped 的服務也會表現得像 Singleton ,這點要注意,因為跟其他的 .net 框架有點差異。但官方建議如果你覺得該使用 Scoped 的服務還是使用 Scoped 吧! |
Singleton | DI 會建立服務的單一實例,所有需要此服務的元件都會得到相同的實例。 |
Transient | 每當元件從服務容器取得服務的實例時,都會收到服務的新實例。 |
而 Blazor WebAssembly 提供了幾個預設服務:
服務 | 存留期 | 描述 |
---|---|---|
HttpClient | Scoped | 提供方法來傳送 HTTP 要求與接收 HTTP 回應,會使用瀏覽器來處理背景中的 HTTP 流量。 |
IJSRuntime | Singleton | 當 JavaScript 呼叫被派遣時,此實例代表 JavaScript Runtime。 |
NavigationManager | Singleton | 包含使用 URIs 和導覽狀態的協助程式。 |
而在元件中注入服務的方式有兩種:
@inject
指示詞注入如:@inject HttpClient HttpClient
[Inject]
屬性注入,一般而言不會這樣使用,除非是注入在基類元件,使得衍生元件不需要再注入。[Inject]
protected HttpClient HttpClient { get; set; }
而在服務中注入其他服務,只能透過建構式注入如:
public class DataAccess : IDataAccess
{
public DataAccess(HttpClient client)
{
// ...
}
}
且要符合以下條件:
必須要有一個建構式,其引數可由 DI 完成,如果有指定預設值,則允許包含 DI 未涵蓋的其他參數。
適用的建構式必須是 public
。
一定要有一個是用的建構式,也就是 DI 在 new 的時候不能有某引數不在 DI Container 內而導致 new 失敗,此時 DI 就會擲回例外狀況。
若是想要將自己的服務放到 DI,只需要修改 Program.cs
檔案中的 Main
方法即可。
透過 builder.Services
並根據服務的生命週期來選擇對應的方法即可:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddSingleton<IMyDependency, MyDependency>();
await builder.Build().RunAsync();
}
}
如上我放入了一個介面為 IMyDependency
實例為 MyDependency
的服務,並且生命週期為 Singleton
,這樣就完成基本的 DI 了。
以上就是關於在 Blazor WebAssembly 使用相依性注入的簡單介紹,是不是很簡單呀?
感謝大家的閱讀,我們明天見。
參考資料
ASP.NET Core Blazor 相依性注入
SOLID (物件導向設計)